42. Mybatis2 - 单表操作, 映射文件获取参数, 多对一查询, 一对多查询, 分布查询, 延迟加载

单表增删改查

mybatis 核心文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<?xml version="1.0" encoding="UTF-8" ?> 
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>
<!-- 加载外部文件到 properties -->
<properties resource="jdbc.properties"></properties>

<!-- 下划线转驼峰 -->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

<typeAliases>
<!-- 表示 com.atguigu.bean 下所有的类都会被创建别名, Mapper xml 里面就可以简写了
只需要写类名或者类名首字母小写,不需要写完整的全限定名。
-->
<package name="com.atguigu.bean"/>
</typeAliases>

<environments default="development">
<environment id="development">
<!-- 使用 JDBC 原生的事务管理方式,即提交和回滚都需要手动处理 -->
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</dataSource>
</environment>
</environments>
<!-- 引入映射文件 -->
<mappers>
<mapper resource="EmpMapper.xml" />
<mapper resource="DeptMapper.xml" />
</mappers>
</configuration>

Emp 实体类对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package com.atguigu.bean;

public class Emp {

private Integer eid;
private String ename;
private Integer age;
private String sex;


public Integer getEid() {
return eid;
}
public void setEid(Integer eid) {
this.eid = eid;
}
public String getEname() {
return ename;
}
public void setEname(String ename) {
this.ename = ename;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public String toString() {
return "Emp [eid=" + eid + ", ename=" + ename + ", age=" + age + ", sex=" + sex + "]";
}
public Emp(Integer eid, String ename, Integer age, String sex) {
super();
this.eid = eid;
this.ename = ename;
this.age = age;
this.sex = sex;
}
public Emp() {
super();
}
}

EmpMapper 映射类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.atguigu.mapper;

import java.util.List;

import com.atguigu.bean.Emp;

public interface EmpMapper {

// 根据 eid 查询员工信息
Emp getEmpByEid(String eid);

// 获取所有的员工信息
List<Emp> getAllEmp();

// 添加员工信息
void addEmp(Emp emp);

// 修改员工信息
void updateEmp(Emp emp);

// 删除员工信息
void deleteEmp(String eid);
}

EmpMapper 映射xml 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.atguigu.mapper.EmpMapper">
<!-- Emp getEmpByEid(String eid); -->
<select id="getEmpByEid" resultType="Emp">
select eid, ename, age, sex from emp where eid = #{eid}
</select>

<!-- List<Emp> getAllEmp();
注意虽然 getAllEmp 返回的是一个 list 集合,但是 resultType 还是写实体类
-->
<select id="getAllEmp" resultType="Emp">
select eid, ename, age, sex from emp
</select>

<!-- void addEmp(Emp emp); -->
<insert id="addEmp">
insert into emp values(null, #{ename}, #{age}, #{sex})
</insert>

<!-- void updateEmp(Emp emp); -->
<update id="updateEmp">
update emp set ename = #{ename}, age = #{age}, sex =#{sex} where eid = #{eid}
</update>

<!-- void deleteEmp(String eid); -->
<delete id="deleteEmp">
delete from emp where eid = #{eid}
</delete>
</mapper>

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package com.atguigu.test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;

import com.atguigu.bean.Emp;
import com.atguigu.mapper.EmpMapper;

public class TestCRUD {
@Test
public void testCURD() throws IOException {
InputStream is = Resources.getResourceAsStream("mybatis-conf.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
// SqlSession sqlSession = sqlSessionFactory.openSession(); 需要手动处理事务
SqlSession sqlSession = sqlSessionFactory.openSession(true); // 自动处理事务(默认为 false 不自动提交)
EmpMapper empMapper = sqlSession.getMapper(EmpMapper.class);

// 测试 getEmpByEid
Emp emp1 = empMapper.getEmpByEid("1");
System.out.println(emp1);

// 测试 getAllEmp
List<Emp> allEmp = empMapper.getAllEmp();
System.out.println(allEmp);

// 测试 addEmp
empMapper.addEmp(new Emp(null, "223333",23, "nv"));

// 测试 updateEmp
empMapper.updateEmp(new Emp(2, "zhangsansan", 33, "nv"));

// 测试 deleteEmp
empMapper.deleteEmp("6");
}
}

返回值为 Integer 或 bool

如果返回值是 Integer 或者 bool,我们只需将 mapper 接口中的返回值改成 Integer 或

bool 即可。mapper.xml 中的 resultType 不用添加。

1
2
3
4
 <!-- void deleteEmp(String eid); -->
<delete id="deleteEmp">
delete from emp where eid = #{eid}
</delete>
1
2
3
4
5
// 删除员工信息(返回 Integer)
Integer deleteEmp(String eid);

// 删除员工信息(返回 Integer)
// Boolean deleteEmp(String eid);
1
2
3
4
5
6
7
// 测试 deleteEmp
// Integer i = empMapper.deleteEmp("6");
// System.out.println(i);

// 测试 deleteEmp
Boolean boolean1 = empMapper.deleteEmp("6");
System.out.println(boolean1);

package 的方式引入映射文件

之前在 mybatis 核心配置文件中引入 mapper.xml 映射文件的时候是一个一个单独引入的,这样很不方便。所以可以使用 package 来引入一个包下面的所有 mapper.xml 映射文件。但是这种写法必须将 mapper接口和 mapper.xml 映射文件放到同一个包下。由于 src 下面 com.atguigu.mapper 下面是存放 mapper 接口类的,所以我们应该将 mapper.xml 文件也放到下面才对,但是 xml 是文件不是类,放到里面不太合适,所以我们直接在 conf 下面也新建 com.atguigu.mapper 包,直接存放里面就行。(因为 src 和 cong 都是资源文件夹 source folder,所以在 ide 里面是分开的,但是最终 src 下的 com.atguigu.mapper 和 conf 下的 com.atguigu.mapper 里面的文件都是在一起的。)

1
2
3
4
5
6
7
8
9
10
<!-- 引入映射文件 -->
<mappers>
<!-- <mapper resource="EmpMapper.xml" />
<mapper resource="DeptMapper.xml" /> -->

<!-- 使用 package 指定路径下的所有映射文件,而不是一个一个的引入。
但是这种写法必须将 mapper接口 和 mapper xml 映射文件放到同一个包下
-->
<package name="com.atguigu.mapper"/>
</mappers>

查询的三种方式

新增 EmpSelectMapper 接口类和 EmpSelectMapper.xml 映射文件演示。

简单查询返回实体对象

1
2
// 根据 eid 查询员工信息
Emp getEmpByEid(String eid);
1
2
3
4
<!-- Emp getEmpByEid(String eid); -->
<select id="getEmpByEid" resultType="Emp">
select eid, ename, age, sex from emp where eid = #{eid}
</select>

查询总记录数量

1
2
// 获取所有的员工数量
Integer getCount();
1
2
3
4
 <!--Integer getCount();-->
<select id="getCount" resultType="Integer">
select count(eid) from emp
</select>

将结果存放在 map 中

1
2
3
4
5
6
// 以 map 集合获取一个员工信息
Map<String, Object> getEmpMapByEid(String eid);

// 以 map 集合获取所有员工信息
@MapKey(value = "eid") // 设置每个 map 的键为 eid
Map<String, Object> getAllEmpMap();
1
2
3
4
5
6
7
8
9
 <!-- Map<String, Object> getEmpMapByEid(String eid); -->
<select id="getEmpMapByEid" resultType="HashMap">
select eid, ename, age, sex from emp where eid = #{eid}
</select>

<!-- Map<String, Object> getAllEmpMap(); -->
<select id="getAllEmpMap" resultType="Emp">
select eid, ename, age, sex from emp
</select>

完整代码如下:

EmpSelectMapper 接口类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.atguigu.mapper;

import java.util.List;
import java.util.Map;

import org.apache.ibatis.annotations.MapKey;

import com.atguigu.bean.Emp;

public interface EmpSelectMapper {

// 根据 eid 查询员工信息
Emp getEmpByEid(String eid);

// 获取所有的员工数量
Integer getCount();

// 以 map 集合获取一个员工信息
Map<String, Object> getEmpMapByEid(String eid);

// 以 map 集合获取所有员工信息
@MapKey(value = "eid") // 设置每个 map 的键为 eid
Map<String, Object> getAllEmpMap();
}

EmpSelectMapper.xml 配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.atguigu.mapper.EmpSelectMapper">
<!-- Emp getEmpByEid(String eid); -->
<select id="getEmpByEid" resultType="Emp">
select eid, ename, age, sex from emp where eid = #{eid}
</select>

<!--Integer getCount();-->
<select id="getCount" resultType="Integer">
select count(eid) from emp
</select>

<!-- Map<String, Object> getEmpMapByEid(String eid); -->
<select id="getEmpMapByEid" resultType="HashMap">
select eid, ename, age, sex from emp where eid = #{eid}
</select>

<!-- Map<String, Object> getAllEmpMap(); -->
<select id="getAllEmpMap" resultType="Emp">
select eid, ename, age, sex from emp
</select>
</mapper>

parameterType

在 mapper.xml 文件中,insert, update 标签和 delete 标签是没有 resultType 属性的。但是可以设置 parameterType,用来指定传入参数的类型,但是在开发中一般不推荐写此属性,因为 mybatis 可以自动推断参数的类型。

1
2
3
4
<!-- void updateEmp(Emp emp); parameterType 一般不写-->
<update id="updateEmp" parameterType="Emp">
update emp set ename = #{ename}, age = #{age}, sex =#{sex} where eid = #{eid}
</update>

获取参数值的两种方式

${}

相当于 Statement 对象,采用的字符串拼接的方式。所以这种方式是没有引号的,需要我们在 mapper.xml 的 SQL 手动的加上引号这样 SQL 才能没问题。

1
2
3
<insert id="insertEmp">
insert into emp values(null, '${ename}', ${age}, '${sex}')
</insert>

#{}

相当于 PreparedStatement,采用的是占位符的方式来拼接 SQL。这种方式是自带有引号的,我们不必手动添加引号。但是这种方式因为会自动加引号,所以在批量删除和批量修改还有模糊查询的时候不能用这种方式。

1
2
3
<insert id="insertEmp">
insert into emp values(null, #{ename}, #{age}, #{sex})
</insert>

大部分情况下我们都选择使用 #{} , 只有在特殊情况下才使用 ${},例如模糊查询和批量删除。

获取新增自增 ID

1
2
3
4
5
6
7
<!-- 
useGeneratedKeys 默认就是 true(但建议使用的时候还是写出来),代表可以使用自动生成的主键
keyProperty 代表将新增的主键值赋值给 实体类中的哪一个字段
-->
<insert id="insertEmp" useGeneratedKeys="true" keyProperty="eid">
insert into emp values(null, #{ename}, #{age}, #{sex})
</insert>
1
2
3
4
5
6
7
8
9
10
11
@Test
public void test() throws IOException {
InputStream is = Resources.getResourceAsStream("mybatis-conf.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = sqlSessionFactory.openSession(true);

ParamMapper paramMapper = sqlSession.getMapper(ParamMapper.class);
Emp emp = new Emp(null, "1111", 20, "nana");
paramMapper.insertEmp(emp);
System.out.println("新插入数据的 eid" + emp.getEid());
}

不同的参数类型取值的方式

  1. 当传输的参数为单个 String 或基本数据类型和其包装类

    #{}: 可以以任意的名字获取参数值。

    ${}: 只能以 ${value}${_parameter} 方式获取。要注意单引号问题。

  2. 当传输参数为 JavaBean

    #{}${} 都可以通过属性名直接获取属性值,但是要注意 ${} 的单引号问题。

  3. 当传输多个参数

    #{}: 可以使用 #{0} #{1} 或者 #{param1} #{param2} 去取值。

    ${}: 只能使用 #{param1} #{param2} 去取值。要注意单引号问题。

    即当传输任意多个参数时,都会被MyBatis重新包装成一个Map传入。Map的key是param1,param2,或者0,1…,值就是参数的值。

  4. 当传输的参数是 Map

    #{}${} 都可以通过键的名字直接获取属性值,但是要注意 ${} 的单引号问题。

  5. 命名参数

    为参数使用 @Param 起一个名字,MyBatis 就会将这些参数封装进 map 中,key 就是我们自己指定的名字。#{}${} 都可以通过键的名字直接获取属性值,但是要注意 ${} 的单引号问题。

  6. 当传输参数为 List/Array 时

    MyBatis 会将 List 或 Array 放在 map 中,List 以 list 为键,Array 以 array 为键。

新建 ParamMapper 接口类和 ParamMapper.xml 映射文件进行演示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.atguigu.mapper;

import java.util.HashMap;
import java.util.Map;

import org.apache.ibatis.annotations.Param;

import com.atguigu.bean.Emp;

public interface ParamMapper {
// 添加员工信息
void insertEmp(Emp emp);

// 根据 eid 获取员工信息
Emp getEmpById(String eid);

// 根据 eid ename 查询员工信息
Emp getEmpByEidAndEname(String eid, String ename);

// 根据 Map 查询员工信息
Emp getEmpByMap(HashMap<String, Object> hashMap);

// 根据 eid ename 查询员工信息
Emp getEmpByEidAndEnameByParam(@Param("eid") String eid, @Param("ename") String ename);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.atguigu.mapper.ParamMapper">
<!-- void insertEmp(Emp emp); -->
<!--
useGeneratedKeys 默认就是 true,代表可以支持自动生成主键的字段
keyProperty 代表将新增的主键值赋值给 实体类中的哪一个字段
-->
<insert id="insertEmp" useGeneratedKeys="true" keyProperty="eid">
insert into emp values(null, #{ename}, #{age}, #{sex})
</insert>

<!-- Emp getEmpById(String eid); -->
<select id="getEmpById" resultType="Emp">
select eid, ename, age, sex from emp where eid = #{eid}
</select>

<!-- getEmpByEidAndEname -->
<select id="getEmpByEidAndEname" resultType="Emp">
select eid, ename, age, sex from emp where eid = #{param1} and ename = #{param2}
</select>

<!-- Emp getEmpByMap(Map<String, Object> map); -->
<select id="getEmpByMap" resultType="Emp">
select eid, ename, age, sex from emp where eid = #{eid} and ename = #{ename}
</select>

<!-- getEmpByEidAndEnameByParam -->
<select id="getEmpByEidAndEnameByParam" resultType="Emp">
select eid, ename, age, sex from emp where eid = #{eid} and ename = #{ename}
</select>
</mapper>

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package com.atguigu.test;

import static org.junit.Assert.*;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;

import com.atguigu.bean.Emp;
import com.atguigu.mapper.EmpSelectMapper;
import com.atguigu.mapper.ParamMapper;

public class TestParam {

@Test
public void test() throws IOException {
InputStream is = Resources.getResourceAsStream("mybatis-conf.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = sqlSessionFactory.openSession(true);

ParamMapper paramMapper = sqlSession.getMapper(ParamMapper.class);
// Emp emp = new Emp(null, "1111", 20, "nana");
// paramMapper.insertEmp(emp);
// System.out.println("新插入数据的 eid" + emp.getEid());

// Emp emp = paramMapper.getEmpById("1");
// System.out.println(emp);

// Emp empByEidAndEname = paramMapper.getEmpByEidAndEname("2", "zhangsansan");
// System.out.println(empByEidAndEname);

// HashMap<String,Object> hashMap = new HashMap<>();
// hashMap.put("eid", 2);
// hashMap.put("ename", "zhangsansan");
// Emp empByMap = paramMapper.getEmpByMap(hashMap);
// System.out.println(empByMap);

Emp empByEidAndEname = paramMapper.getEmpByEidAndEname("2", "zhangsansan");
System.out.println(empByEidAndEname);
}
}

自定义映射(多对一查询)

对个员工对应一个部门。

方式1

在 Emp 实体类中新增 Dept 字段属性,表示员工所属的部门信息。要实现查询所有的员工,并显示所在的部门信息。

我们可以在 mapper.xml 中设置 resultMap 来实现自定义映射。用来处理复杂的表关系。id 标签用于设置主键的映射关系,其中的 column 设置的是字段名,property 设置的是属性名字。非主键需要使用 result 标签。这种方式不常用。

定义 EmpDeptMapper 接口类

1
2
3
4
5
6
7
8
9
10
11
package com.atguigu.mapper;

import java.util.List;

import com.atguigu.bean.Emp;

public interface EmpDeptMapper {

// 获取所有的员工对象
List<Emp> getAllEmp();
}

定义 EmpDeptMapper.xml 映射文件。并在其中定义自定义返回映射 resultMap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.atguigu.mapper.EmpDeptMapper">
<!-- 自定义映射(一般使用得很少) -->
<resultMap type="Emp" id="empMap">
<!-- 主键使用 id -->
<id column="eid" property="eid"/>
<!-- 非主键使用 result -->
<result column="ename" property="ename"/>
<result column="sex" property="sex"/>
<result column="did" property="dept.did"/>
<result column="dname" property="dept.dname"/>
<result column="age" property="age"/>
</resultMap>

<!-- List<Emp> getAllEmp(); -->
<select id="getAllEmp" resultMap="empMap">
select e.eid,e.ename,e.age,e.sex,e.did,d.dname from emp e left join dept d on e.did= d.did
</select>
</mapper>

方式2

使用 resultMap 和 association 来完成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.atguigu.mapper.EmpDeptMapper">

<!-- 自定义映射(一般使用得很少) -->
<!-- <resultMap type="Emp" id="empMap">
主键使用 id
<id column="eid" property="eid"/>
非主键使用 result
<result column="ename" property="ename"/>
<result column="sex" property="sex"/>
<result column="did" property="dept.did"/>
<result column="dname" property="dept.dname"/>
<result column="age" property="age"/>
</resultMap> -->

<resultMap type="Emp" id="empMap">
<id column="eid" property="eid"/>
<result column="ename" property="ename"/>
<result column="sex" property="sex"/>
<result column="age" property="age"/>
<association property="dept" javaType="Dept">
<id column="did" property="did"/>
<result column="dname" property="dname"/>
</association>
</resultMap>

<!-- List<Emp> getAllEmp(); -->
<select id="getAllEmp" resultMap="empMap">
select e.eid,e.ename,e.age,e.sex,e.did,d.dname from emp e left join dept d on e.did= d.did
</select>

</mapper>

实体类,接口类和测试类都和方式1一样,只是这里使用 resultMap 和 association 来实现。

association 分步查询/延迟加载

分步查询

对于查询员工信息并且将对应的部门信息也查询出来的需求,就可以通过分步的方式完成查询。

  1. 先通过员工的id查询员工信息

  2. 再通过查询出来的员工信息中的外键(部门id)查询对应的部门信息.

先通过员工 id 查询员工信息, EmpDeptMapper 接口中的方法为

1
2
// 分步查询。先查询 Emp 信息,然后在通过 Emp 信息查询 Dept 信息。
Emp getEmpStep(String eid);

EmpDeptMapper 中 xml 的配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- 分步查询。这里表示先通过eid查询员工信息,然后在拿着员工信息中的did去Dept中查询对应的dept信息 -->
<resultMap type="Emp" id="empMapStep">
<id column="eid" property="eid"/>
<result column="ename" property="ename"/>
<result column="sex" property="sex"/>
<result column="age" property="age"/>
<!--
select 表示分步查询的 SQL,值是接口的全限定名.方法名。
column 表示要将哪个字段的值传入 getDeptByDid 中进行查询
-->
<association property="dept" select="com.atguigu.mapper.DeptMapper.getDeptByDid" column="did"/>
</resultMap>
<select id="getEmpStep" resultMap="empMapStep">
select eid, ename, age, sex, did from emp where eid = #{eid}
</select>

com.atguigu.mapper.DeptMapper 中的 getDeptByDid 接口如下。

1
2
3
4
5
6
7
package com.atguigu.mapper;

import com.atguigu.bean.Dept;

public interface DeptMapper {
Dept getDeptByDid(String did);
}
1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.atguigu.mapper.DeptMapper">
<!-- Dept getDeptByDid(String did); -->
<select id="getDeptByDid" resultType="Dept">
select did, dname from dept where did = #{did}
</select>
</mapper>

延迟加载

只对分布查询有效果,例如上面的例子中,emp 中包含了 dept 对象,如果只用到了 emp 中的信息那么就先查询 emp 的信息,如果用到了 dept 信息那么才会去查询 dept。

开启延迟加载需要在 mybits 的核心配置文件中进行设置。

1
2
3
4
5
6
7
8
<settings>
<!-- 下划线转驼峰 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!-- 开启延迟加载 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 设置加载的数据是按需还是全部 -->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
1
2
3
4
5
 // 只去查询 emp 信息
Emp empStep = empDeptMapper.getEmpStep("2");
System.out.println(empStep.getEname());
// 用到了 dept 才去执行 dept 的查询语句
System.out.println(empStep.getDept().getDname());

自定义映射(一对多查询)

一个部门对应对个员工

在 EmpDeptMapper 接口类中定义 getDeptEmpsByDid 方法。

1
2
// 获取 dept 的信息,包括里面的员工信息
Dept getDeptEmpsByDid(String did);

定义 mapper.xml 映射文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!--Dept getDeptEmpsByDid(String did);  -->
<resultMap type="Dept" id="deptMap">
<id column="did" property="did"/>
<result column="dname" property="dname"/>
<!--
注意这里和多对一不同,这里是一对多,所以这里要用 collection。
而且这里不用 javaType,而要用 ofType,用来表示 collection 中存的是什么
-->
<collection property="emps" ofType="Emp">
<id column="eid" property="eid"/>
<result column="ename" property="ename"/>
<result column="age" property="age"/>
<result column="sex" property="sex"/>
</collection>
</resultMap>
<select id="getDeptEmpsByDid" resultMap="deptMap">
select d.did, d.dname, e.eid, e.ename, e.age, e.sex from dept d left join emp e on d.did = e.did where d.did = #{did}
</select>

collection 是用来处理一对多和多对多的关系的,其中的 ofType 指的是集合中数据的类型。collection 中不需要指定 javaType。

测试代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package com.atguigu.test;

import static org.junit.Assert.*;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;

import com.atguigu.bean.Dept;
import com.atguigu.bean.Emp;
import com.atguigu.mapper.EmpDeptMapper;
import com.atguigu.mapper.EmpSelectMapper;
import com.atguigu.mapper.ParamMapper;

public class TestEmpDept {

@Test
public void test() throws IOException {
InputStream is = Resources.getResourceAsStream("mybatis-conf.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = sqlSessionFactory.openSession(true);

EmpDeptMapper empDeptMapper = sqlSession.getMapper(EmpDeptMapper.class);
// List<Emp> allEmp = empDeptMapper.getAllEmp();
// System.out.println(allEmp);

// 只去查询 emp 信息
// Emp empStep = empDeptMapper.getEmpStep("2");
// System.out.println(empStep.getEname());
// 用到了 dept 才去执行 dept 的查询语句
// System.out.println(empStep.getDept().getDname());

Dept deptEmpsByDid = empDeptMapper.getDeptEmpsByDid("3");
System.out.println(deptEmpsByDid);
}
}

collection 分步查询/延迟加载

分步查询

对于查询部门信息并且将对应的员工信息也查询出来的需求,就可以通过分步的方式完成查询。

  1. 先通过部门的id查询部门信息
  2. 再通过查询出来的部门信息中的外键(员工id)查询对应的员工信息.

先通过部门 id 查询部门信息, EmpDeptMapper 接口中的方法为

1
2
// 分步查询。先查询 Dept 信息,然后在通过 Dept 信息查询 Emp 信息。
Dept getOnlyDeptByDid(String did);

EmpDeptMapper 中 xml 的配置如下:

写法和 association 中的分步查询中类似的,只不过一个是 association 中写 select 和 column,这个是 collection 中写 select 和 column。

1
2
3
4
5
6
7
8
9
<!-- Dept getOnlyDeptByDid(String did); -->
<resultMap type="Dept" id="deptMapStep">
<id column="did" property="did"/>
<result column="dname" property="dname"/>
<collection property="emps" select="com.atguigu.mapper.EmpMapper.getEmpListByDid" column="did"/>
</resultMap>
<select id="getOnlyDeptByDid" resultMap="deptMapStep">
select did, dname from dept where did = #{did}
</select>

在看 com.atguigu.mapper.EmpMapper 抽象类中的 getEmpListByDid 方法的定义

1
2
// 根据部门 id 查询所属该部门的员工
List<Emp> getEmpListByDid(String did);

对应的 EmpMapper.xml 中的内容为:

1
2
3
4
<!-- List<Emp> getEmpListByDid(String did); -->
<select id="getEmpListByDid" resultType="Emp">
select eid, ename, age, sex, did from emp where did = #{did}
</select>

测试代码:

1
2
Dept onlyDeptByDid = empDeptMapper.getOnlyDeptByDid("3");
System.out.println(onlyDeptByDid.getDname());

这样我们就完成了一对多中的分布查询,通过先查询 dept 中的信息,然后在通过 dept 中的外键 id 信息去到另一个 sql 中查询 emp 信息。

延迟加载

因为上面中配置了延迟加载的配置,所以这里的查询也是延迟加载的。但是我们可以单独设置是否需要延迟加载,可以在 collection 标签或者 association 中加上 fetchType 属性。默认 fetchType 的值为 lazy,即允许延迟加载。改为 eager 就不使用延迟加载。

1
2
3
4
5
6
7
8
9
<!-- Dept getOnlyDeptByDid(String did); -->
<resultMap type="Dept" id="deptMapStep">
<id column="did" property="did"/>
<result column="dname" property="dname"/>
<collection property="emps" select="com.atguigu.mapper.EmpMapper.getEmpListByDid" column="did" fetchType="eager"/>
</resultMap>
<select id="getOnlyDeptByDid" resultMap="deptMapStep">
select did, dname from dept where did = #{did}
</select>

代码地址